// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#include "JitRecompiler.h"
#include "JitFunctionContext.h"

#include <llvm/Support/StandardPasses.h>
#include <llvm/Support/TypeBuilder.h>
#include <llvm/DerivedTypes.h>
#include <llvm/BasicBlock.h>
#include <llvm/Function.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Target/TargetData.h>

#include <set>
#include <stack>
#include <cstdio>

#include "../PowerPC.h"
#include "../PPCTables.h"
#include "../../HW/Memmap.h"

#pragma mark General implementation of JitRecompiler
namespace LLVMJit
{

JitRecompiler::JitRecompiler(llvm::Module* module, const llvm::TargetData* targetData)
	: context(module->getContext())
{
	this->module = module;
	passManager = new llvm::FunctionPassManager(module);
	passManager->add(new llvm::TargetData(*targetData));
	llvm::createStandardFunctionPasses(passManager, 3);
	RemapInstructions();
}

void JitRecompiler::SetBindings(JitBindings* bindings)
{
	this->bindings = bindings;
}

void JitRecompiler::ClearCache()
{
	typedef std::map<u32, llvm::Function*>::iterator map_iterator;
	for (map_iterator iter = functionCache.begin(); iter != functionCache.end(); iter++)
		delete iter->second;
	functionCache.clear();
}

void JitRecompiler::ClearCache(u32 address)
{
	if (functionCache.count(address) == 0)
		return;
	
	delete functionCache[address];
	functionCache.erase(address);
}

llvm::Module* JitRecompiler::GetModule()
{
	return module;
}

llvm::Function* JitRecompiler::GetFunction(u32 address)
{
	if (functionCache.count(address) == 0)
		RecompileFunction(address);
	return functionCache[address];
}

llvm::Function* JitRecompiler::GetFunction(UGeckoInstruction instruction)
{
	char functionName[33];
	sprintf(functionName, "(anonymous function: 0x%08x)", instruction.hex);
	
	JitFunctionContext functionContext(module, functionName);
	llvm::IRBuilder<> builder = functionContext.GetBuilder();
	builder.SetInsertPoint(functionContext.GetEntryBlock());
	
	// no SetPC because we don't know where the instruction is
	
	GekkoOPInfo* info = GetOpInfo(instruction);
	functionContext.AddCycles(info->numCyclesMinusOne);
	EmitILForInstruction(instruction, functionContext);
	
	builder.CreateRetVoid();
	
	// no need to optimize single-instruction functions
	return functionContext.GetFunction();
}

llvm::Function* JitRecompiler::GetCachedFunction(u32 address)
{
	if (functionCache.count(address) == 0)
		return NULL;
	return functionCache[address];
}

JitRecompiler::~JitRecompiler()
{
	ClearCache();
	delete passManager;
}

#pragma mark -
#pragma mark Recompilation
llvm::Function* JitRecompiler::RecompileFunction(u32 address)
{
	char functionName[13];
	sprintf(functionName, "ppc$%08x", address);
	
	JitFunctionContext functionContext(module, functionName);
	
	// do this now so we avoid infinite recursion if we meet recursion
	functionCache[address] = functionContext.GetFunction();
	
	// Since looping to the first block is not allowed in LLVM IR, we make a
	//	dummy block after the first one.
	llvm::IRBuilder<> builder = functionContext.GetBuilder();
	builder.SetInsertPoint(functionContext.GetEntryBlock());
	builder.CreateBr(functionContext.GetBlock(address));
	
	functionContext.PushBranch(address);
	while (functionContext.HasMoreBranches())
	{
		u32 currentAddress = functionContext.PopBranch();
		builder.SetInsertPoint(functionContext.GetBlock(currentAddress));
		
		while (true)
		{
			u32 opcode = Memory::ReadUnchecked_U32(currentAddress);
			UGeckoInstruction instruction(opcode);
			functionContext.SetPC(currentAddress);
			// set PC in the JIT code
			StoreGReg(functionContext.GetBuilder(), 3, builder.getInt32(currentAddress));
			
			if (!PPCTables::IsValidInstruction(instruction))
				// oh crap, can't recompile this
				return NULL;
			
			GekkoOPInfo* info = GetOpInfo(instruction);
			functionContext.AddCycles(info->numCyclesMinusOne);
			EmitILForInstruction(instruction, functionContext);
		}
	}
	
	llvm::Function* resultingFunction = functionContext.GetFunction();
	// verifyFunction returns true if an error was found
	if (llvm::verifyFunction(*resultingFunction, llvm::PrintMessageAction) == true)
	{
		PanicAlert("Recompiling failed: function is not well-formed. The logs should have a more detailed message.");
		abort();
	}
	passManager->run(*resultingFunction);
	return resultingFunction;
}

void JitRecompiler::EmitILForInstruction(UGeckoInstruction instruction, JitFunctionContext& context)
{
	InstructionRecompiler instructionFunction = dispatchTable[instruction.OPCD];
	(this->*instructionFunction)(instruction, context);
}

}